/*
 * CCVisu is a tool for visual graph clustering
 * and general force-directed graph layout.
 * This file is part of CCVisu.
 *
 * Copyright (C) 2005-2011  Dirk Beyer
 *
 * CCVisu is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public License
 * as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * CCVisu is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with CCVisu; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 * Please find the GNU Lesser General Public License in file
 * license_lgpl.txt or http://www.gnu.org/licenses/lgpl.txt
 *
 * Dirk Beyer    (firstname.lastname@uni-passau.de)
 * University of Passau, Bavaria, Germany
 */
package org.sosy_lab.ccvisu.ui.controlpanel;

import java.awt.BorderLayout;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.text.DecimalFormat;
import java.util.logging.Level;

import javax.swing.AbstractAction;
import javax.swing.DefaultComboBoxModel;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JFileChooser;
import javax.swing.JFormattedTextField;
import javax.swing.JPanel;
import javax.swing.JSpinner;
import javax.swing.JTextField;
import javax.swing.KeyStroke;

import org.sosy_lab.ccvisu.CCVisuController;
import org.sosy_lab.ccvisu.CCVisuOptions;
import org.sosy_lab.ccvisu.layout.MinimizerBarnesHut;
import org.sosy_lab.ccvisu.layout.MinimizerOptions;
import org.sosy_lab.ccvisu.readers.InputFormat;
import org.sosy_lab.ccvisu.readers.ReaderData;
import org.sosy_lab.common.LogManager;
import org.sosy_lab.common.configuration.Configuration;
import org.sosy_lab.common.configuration.InvalidConfigurationException;
import org.sosy_lab.util.interfaces.WorkerManager;

public class ToolPanelLayouter extends ControlPanel {
  private static final long   serialVersionUID            = 1L;

  private JCheckBox           weightedCheckbox            = new JCheckBox();
  private JFormattedTextField gravitationTextField        =
      new JFormattedTextField(DecimalFormat.getInstance());

  // generic since Java 7; we require Java 6; see svn r825 for comparison
  @SuppressWarnings("rawtypes")
  private JComboBox           repulsionTypeTextField      = new JComboBox();

  private JFormattedTextField repulsionExponentTextField  = new JFormattedTextField();
  private JFormattedTextField attractionExponentTextField = new JFormattedTextField();
  private JSpinner            numOfIterationsSpinner      = new JSpinner();
  private JSpinner            dimensionsSpinner           = new JSpinner();
  private JFormattedTextField scaleTextField              = new JFormattedTextField();
  private JButton             chooseInitialLayoutButton   = new JButton();
  private JTextField          initialLayoutTextField      = new JTextField();
  private JButton             startMinimizerButton        = new JButton("Run layouter");
  private JCheckBox           animationCheckBox           = new JCheckBox();

  private CCVisuOptions       options                     = null;
  private WorkerManager       workerManager               = null;

  private CCVisuController    controller;

  private Configuration       config;

  private final LogManager    logger;

  private MinimizerOptions    minimizerOptions;

  /**
   * @throws InvalidConfigurationException
   */
  public ToolPanelLayouter(CCVisuOptions pOptions, WorkerManager pWorkerManager,
      CCVisuController controller, Configuration config, LogManager logger)
          throws InvalidConfigurationException {

    this.options = pOptions;
    this.logger = logger;
    this.config = config;
    this.workerManager = pWorkerManager;
    this.controller = controller;

    minimizerOptions = new MinimizerOptions(config);

    initComponents();
  }

  @Override
  public void applyOptions() {
    //options.getOption(OptionsEnum.timeWindow).set(Integer.parseInt(timeWindow.getText()));
    controller.setDimension(((Integer) dimensionsSpinner.getValue()).intValue());
    minimizerOptions.setNumberOfIterations(Integer.parseInt(numOfIterationsSpinner.getValue().toString()));
    controller.setInitialLayout(initialLayoutTextField.getText());
    minimizerOptions.setAttractionExp((Float) attractionExponentTextField.getValue());
    minimizerOptions.setRepulsionExp((Float) repulsionExponentTextField.getValue());
    minimizerOptions.setGravitation((Float) gravitationTextField.getValue());
    minimizerOptions.setUseVertexRepulsion((repulsionTypeTextField.getSelectedIndex() == 1));
    minimizerOptions.setUseUnweightedModel(!weightedCheckbox.isSelected());
    options.setScalePos((Float) scaleTextField.getValue());
    minimizerOptions.setAnim(animationCheckBox.isSelected());
  }

  @Override
  public void loadOptions() {
    scaleTextField.setValue(new Float(options.getScalePos()));
    dimensionsSpinner.setValue(controller.getDimension());
    numOfIterationsSpinner.setValue(minimizerOptions.getNumberOfIterations());
    initialLayoutTextField.setText(controller.getInitialLayout());
    attractionExponentTextField.setValue(new Float(minimizerOptions.getAttractionExp()));
    gravitationTextField.setValue(new Float(minimizerOptions.getGravitation()));
    repulsionExponentTextField.setValue(new Float(minimizerOptions.getRepulsionExp()));
    weightedCheckbox.setSelected(!minimizerOptions.useUnweightedModel());
    animationCheckBox.setSelected(minimizerOptions.isAnim());

    if (minimizerOptions.useVertexRepulsion()) {
      repulsionTypeTextField.setSelectedIndex(1);
    } else {
      repulsionTypeTextField.setSelectedIndex(0);
    }
  }

  public void initComponents() {
    JPanel initLayoutPanel = new JPanel(new BorderLayout());
    initLayoutPanel.add(initialLayoutTextField, BorderLayout.CENTER);
    initLayoutPanel.add(chooseInitialLayoutButton, BorderLayout.EAST);

    JPanel controlPanel = new JPanel();
    controlPanel.add(startMinimizerButton);

    JPanel paramsPanel = new JPanel();
    paramsPanel.setLayout(new GridBagLayout());
    addOptionControls(paramsPanel, "Weighted?", weightedCheckbox);
    addOptionControls(paramsPanel, "Animation?", animationCheckBox);
    addOptionControls(paramsPanel, "Attraction exponent:", attractionExponentTextField);
    addOptionControls(paramsPanel, "Repulsion exponent:", repulsionExponentTextField);
    addOptionControls(paramsPanel, "Repulsion type:", repulsionTypeTextField);
    addOptionControls(paramsPanel, "Gravitation:", gravitationTextField);
    addOptionControls(paramsPanel, "Initial layout:", initLayoutPanel);
    addOptionControls(paramsPanel, "Dimensions:", dimensionsSpinner);
    addOptionControls(paramsPanel, "Scale factor:", scaleTextField);
    addOptionControls(paramsPanel, "Number of iterations:", numOfIterationsSpinner);

    this.setLayout(new BorderLayout());
    this.add(paramsPanel, BorderLayout.CENTER);
    this.add(controlPanel, BorderLayout.SOUTH);

    animationCheckBox.setText("Yes");
    repulsionTypeTextField.setModel(
        new DefaultComboBoxModel(new String[] { "Edge repulsion", "Vertex repulsion" }));
    chooseInitialLayoutButton.setText("Choose...");
    weightedCheckbox.setText("Yes");

    setActionOnEnter(attractionExponentTextField);
    setActionOnEnter(repulsionExponentTextField);
    setActionOnEnter(gravitationTextField);
    setActionOnEnter(scaleTextField);
    setActionOnEnter(initialLayoutTextField);
    setActionOnEnter(dimensionsSpinner);
    setActionOnEnter(numOfIterationsSpinner);

    startMinimizerButton.addActionListener(new ActionListener() {
      @Override
      public void actionPerformed(ActionEvent e) {
        applyAndMinimize();
      }
    });

    chooseInitialLayoutButton.addActionListener(new ActionListener() {
      @Override
      public void actionPerformed(ActionEvent evt) {
        JFileChooser fileDialog = new JFileChooser(".");
        fileDialog.setFileFilter(ReaderData.mkExtensionFileFilter(InputFormat.LAY));
        int outcome = fileDialog.showOpenDialog(null);
        if (outcome == JFileChooser.APPROVE_OPTION) {
          assert (fileDialog.getCurrentDirectory() != null);
          assert (fileDialog.getSelectedFile() != null);
          String fileName =
            fileDialog.getCurrentDirectory().toString() + File.separator
            + fileDialog.getSelectedFile().getName();
          initialLayoutTextField.setText(fileName);
        }
      }
    });
  }

  private void applyAndMinimize() {
    applyOptions();

    try {
      MinimizerBarnesHut minimizer = new MinimizerBarnesHut(minimizerOptions, options.getGraph(), config, logger);
      workerManager.addAndRunTask(minimizer, "Minimizer");
    } catch (InvalidConfigurationException e) {
      logger.logUserException(Level.WARNING, e, "Could not create "
          + "MinimizerBarnesHut due to invalid configuration.");
    }
  }

  private void setActionOnEnter(JComponent component) {
    component.getInputMap().put(KeyStroke.getKeyStroke('\n'), "runMinimizer");
    component.getActionMap().put("runMinimizer", new AbstractAction() {
      @Override
      public void actionPerformed(ActionEvent e) {
        applyAndMinimize();
      }
    });
  }
}
